package com.robertux.leaderboard.process;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.persistence.EntityManager;
import javax.persistence.Persistence;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import com.robertux.leaderboard.beans.EstadoEntidad;
import com.robertux.leaderboard.beans.entities.Meta;
import com.robertux.leaderboard.beans.entities.external.Sucursal;
import com.robertux.leaderboard.util.ResourceLocator;
import com.robertux.leaderboard.util.gestoresModelos.GestorEntidades;
import com.robertux.leaderboard.util.gestoresModelos.GestorEntidadesFarmacia;
import com.robertux.leaderboard.util.gestoresModelos.GestorMetas;

public class ProgressUpdater {
	private Logger logger;
	Properties mysqlConnProps;
	Properties sqlServerConnProps;
	private EntityManager eMgr;
	private GestorMetas gMetas;
	private GestorEntidadesFarmacia gEntFrm;
	
	public ProgressUpdater() {
		Properties log4jProps = ResourceLocator.loadProperties("log4j.properties");
		PropertyConfigurator.configure(log4jProps);
		this.logger = Logger.getLogger(this.getClass());
		
		this.mysqlConnProps = ResourceLocator.loadProperties("mysqlConnection.properties");
		this.sqlServerConnProps = ResourceLocator.loadProperties("sqlserverConnection.properties");
		
		this.logger.debug("Propiedades cargadas para MySQL: " + mysqlConnProps.toString());
		this.logger.debug("Propiedades cargadas para SQL Server: " + sqlServerConnProps.toString());
		
		try {
			this.eMgr = Persistence.createEntityManagerFactory(MetasUpdater.LEADERBOARDPUPD_PU).createEntityManager();
			this.gMetas = new GestorMetas(new GestorEntidades<Meta>(eMgr));
			this.gEntFrm = new GestorEntidadesFarmacia(this.sqlServerConnProps);
		} catch (Exception e) {
			this.logger.error("Error tratando de configurar las conexiones a las bases de datos: " + e.getMessage(), e);
		}
	}

	public void updateProgresos() {
		this.updateProgresos(new GregorianCalendar());
	}
	
	public void updateProgresos(Calendar calFecha) {
		long timeStart = System.currentTimeMillis();
		this.logger.info("============================== INICIANDO PROCESO DE ACTUALIZACION DE PROGRESOS ==============================");
		int threads = Runtime.getRuntime().availableProcessors() * 4;
		this.logger.debug("Procesos paralelos disponibles: " + threads);
		ExecutorService service = Executors.newFixedThreadPool(threads);
		
		//Actualización de progresos de metas
		List<Meta> metas = this.gMetas.getMetas(EstadoEntidad.ACTIVO);
		this.logger.info(metas.size() + " metas obtenidas");
		if(metas != null) {
			for(int i=0; i<metas.size(); i++) {
				service.submit(new MetasUpdater(calFecha, this.sqlServerConnProps, this.mysqlConnProps, metas.get(i).getId(), i, metas.size()));
			}
		}
		
		//Actualización de proyecciones de sucursales
		List<Sucursal> sucs = this.gEntFrm.getSucursales();
		if(sucs != null) {
			for(int i=0; i<sucs.size(); i++) {
				service.submit(new ProyeccionesUpdater(calFecha, sqlServerConnProps, sucs.get(i), i, sucs.size()));
			}
		}
		
		service.shutdown();
		try {
			service.awaitTermination(8, TimeUnit.HOURS);
		} catch (InterruptedException e) {
			this.logger.error("Error tratando de esperar la terminación de los procesos paralelos de ejecución", e);
		}
		
		Calendar calHoy = new GregorianCalendar();
		
		//Vencimiento de metas el primer dia de cada mes, despues de haber calculado sus progresos
		if(calHoy.get(Calendar.DAY_OF_MONTH) == 1) {
			this.logger.info("Primer día del mes. Cambiando de estado las metas actuales de " + EstadoEntidad.ACTIVO + " a " + EstadoEntidad.VENCIDO);
			int metasCambiadas = this.gMetas.cambiarEstadoMetas(EstadoEntidad.ACTIVO, EstadoEntidad.VENCIDO);
			this.logger.info(metasCambiadas + " metas cambiadas");
		}
		
		long timeDiff = System.currentTimeMillis() - timeStart;
		this.logger.info("============================== FINALIZANDO PROCESO DE ACTUALIZACION DE PROGRESOS ==============================");
		
		Calendar calDuracion = new GregorianCalendar();
		calDuracion.set(Calendar.HOUR, 0);
		calDuracion.set(Calendar.MINUTE, 0);
		calDuracion.set(Calendar.SECOND, 0);
		calDuracion.set(Calendar.MILLISECOND, 0);
		
		calDuracion.set(Calendar.MILLISECOND, (int)timeDiff);
		this.logger.info("El proceso se completó en " + calDuracion.get(Calendar.HOUR) + " horas, " + calDuracion.get(Calendar.MINUTE) + " minutos y " + calDuracion.get(Calendar.SECOND) + " segundos");
	}
}
